/* $Header: mazewar.h,v 1.7 88/08/25 09:59:51 kent Exp $ */

/*
 * mazewar.h - Definitions for MazeWar
 *
 * Author:	Christopher A. Kent
 * 		Western Research Laboratory
 * 		Digital Equipment Corporation
 * Date:	Wed Sep 24 1986
 */

/* Modified by Michael Greenwald for CS244B, Mar 1992,
   Greenwald@cs.stanford.edu */

/* Modified by Nicholas Dovidio for CS244B, Mar 2009,
 * ndovidio@stanford.edu
 * This version now uses the CS249a/b style of C++ coding.
 */

/***********************************************************
Copyright 1986 by Digital Equipment Corporation, Maynard, Massachusetts,

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Digital not be
used in advertising or publicity pertaining to disstribution of the
software without specific, written prior permission.

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#ifndef MAZEWAR_H
#define MAZEWAR_H


#include "fwk/NamedInterface.h"

#include "Nominal.h"
#include "Exception.h"
#include <string>
/* fundamental constants */

#ifndef	TRUE
#define	TRUE		1
#define	FALSE		0
#endif	/* TRUE */

/* You can modify this if you want to */
#define	MAX_RATS	8

/* network stuff */
/* Feel free to modify.  This is the simplest version we came up with */

/* A unique MAZEPORT will be assigned to your team by the TA */
#define	MAZEPORT	5034
/* The multicast group for Mazewar is 224.1.1.1 */
#define MAZEGROUP       0xe0010101
#define	MAZESERVICE	"mazewar244B"

/* The next two >must< be a power of two, because we subtract 1 from them
   to get a bitmask for random()
 */
#define	MAZEXMAX	32
#define	MAZEYMAX	16
#define	VECTORSIZE	55
#define	NAMESIZE	20
#define	NDIRECTION	4
#define	NORTH		0
#define	SOUTH		1
#define	EAST		2
#define	WEST		3
#define	NVIEW		4
#define	LEFT		0
#define	RIGHT		1
#define	REAR		2
#define	FRONT		3

/* types */

typedef	struct sockaddr_in			Sockaddr;
typedef bool	               		MazeRow[MAZEYMAX];
typedef	MazeRow						MazeType [MAZEXMAX];
typedef	MazeRow						*MazeTypePtr;
//typedef	short						Direction;
typedef	struct {short	x, y; }		XYpoint;
typedef	struct {XYpoint	p1, p2;}	XYpair;
typedef	struct {short	xcor, ycor;}XY;
typedef	struct {unsigned short	bits[16];}	BitCell;
typedef	char						RatName[NAMESIZE];

class MissileInstance;
class MSGQueueInstance;
class MSGQueue;
class MazewarInstance;

int getCurrentTime(void);

extern int TimeZero;


 	class Direction : public Ordinal<Direction, short> {
	public:
		Direction(short num) : Ordinal<Direction, short>(num) {
			if(num<NORTH || num>NDIRECTION){
				throw RangeException("Error: Unexpected value.\n");
			}
		}
	};

 	class Loc : public Ordinal<Loc, short> {
	public:
		Loc(short num) : Ordinal<Loc, short>(num) {
			if(num<0){
				throw RangeException("Error: Unexpected negative value.\n");
			}
		}
	};

 	class Score : public Ordinal<Score, int> {
	public:
		Score(int num) : Ordinal<Score, int>(num) {}
	};


 	class RatIndexType : public Ordinal<RatIndexType, int> {
	public:
		RatIndexType(int num) : Ordinal<RatIndexType, int>(num) {
			if(num<0){
				throw RangeException("Error: Unexpected negative value.\n");
			}
		}
	};

 	class RatId : public Ordinal<RatId, unsigned short> {
	public:
		RatId(unsigned short num) : Ordinal<RatId, unsigned short>(num) {
		}
	};

 	class TokenId : public Ordinal<TokenId, long> {
	public:
		TokenId(long num) : Ordinal<TokenId, long>(num) {}
	};


class RatAppearance{

	public:
		RatAppearance() :  x(1), y(1), tokenId(0) {};
		bool	visible;
		Loc	x, y;
		short	distance;
		TokenId	tokenId;
};

/* Rat class contains information about a rat/player, including status, missile, etc */
class Rat{

public:
	Rat() :  playing(0), x(1), y(1), dir(NORTH), score(0), time(-1), messageID(0), 
			nearby(FALSE), missile(NULL), ratIP(1), ratPort(1) { strcpy (name, "Dummy");}	
	bool playing;		/* Indicate whether the rat is active */
	Loc	x, y;			/* Position of last status update */
	Direction dir;		/* Direction of last status update */
	Score score;		/* The score of the rat */
	
	int time;			/* Time of last status update */
	
	unsigned int	messageID; 	/* Most recent message ID. Used to detect out of order packet */
	bool nearby;		/* Whether the rat is near me */
	
	RatName name;		/* The name of the rat */
	MissileInstance* missile;	/* The missile fired by the rat. If no missile, its value is NULL */
	
	unsigned int ratIP;		/* The IP address of the player */
	unsigned int ratPort;	/* The Port number of the player */
	
	/* Update the status of the rat upon receiving new status update */
	void setStatus(Loc _x, Loc _y, Direction _dir, Score _score);
		
	/* When a rat is respawning, the status is set to 0 so the rat will 
		not be visible to others; the score will not change */
	void respawn();
};

typedef	RatAppearance			RatApp_type [MAX_RATS];
typedef	RatAppearance *			RatLook;

/* defined in display.c */
extern RatApp_type 			Rats2Display;

/* variables "exported" by the mazewar "module" */
class MazewarInstance :  public Fwk::NamedInterface  {
 public:
    typedef Fwk::Ptr<MazewarInstance const> PtrConst;
    typedef Fwk::Ptr<MazewarInstance> Ptr;

	static MazewarInstance::Ptr mazewarInstanceNew(string s){
      MazewarInstance * m = new MazewarInstance(s);
      return m;
    }

    inline Direction dir() const { return dir_; }
    void dirIs(Direction dir) { this->dir_ = dir; }
    inline Direction dirPeek() const { return dirPeek_; }
    void dirPeekIs(Direction dirPeek) { this->dirPeek_ = dirPeek; }

    inline long mazePort() const { return mazePort_; }
    void mazePortIs(long  mazePort) { this->mazePort_ = mazePort; }
    inline Sockaddr* myAddr() const { return myAddr_; }
    void myAddrIs(Sockaddr *myAddr) { this->myAddr_ = myAddr; }
    inline RatId myRatId() const { return myRatId_; }
    void myRatIdIs(RatId myRatId) { this->myRatId_ = myRatId; }

    inline bool peeking() const { return peeking_; }
    void peekingIs(bool peeking) { this->peeking_ = peeking; }
    inline int theSocket() const { return theSocket_; }
    void theSocketIs(int theSocket) { this->theSocket_ = theSocket; }
    inline Score score() const { return score_; }
    void scoreIs(Score score) { this->score_ = score; }
    inline Loc xloc() const { return xloc_; }
    void xlocIs(Loc xloc) { this->xloc_ = xloc; }
    inline Loc yloc() const { return yloc_; }
    void ylocIs(Loc yloc) { this->yloc_ = yloc; }
    inline Loc xPeek() const { return xPeek_; }
    void xPeekIs(Loc xPeek) { this->xPeek_ = xPeek; }
    inline Loc yPeek() const { return yPeek_; }
    void yPeekIs(Loc yPeek) { this->yPeek_ = yPeek; }
    inline int active() const { return active_; }
    void activeIs(int active) { this->active_ = active; }
    inline Rat rat(RatIndexType num) const { return mazeRats_[num.value()]; }
    void ratIs(Rat rat, RatIndexType num) { this->mazeRats_[num.value()] = rat; }
    inline unsigned int myIP() { return myIP_;}
    void myIPIs(unsigned int myIP) { this->myIP_ = myIP; }
    inline unsigned int myPort() { return myPort_; }
    void myPortIs(unsigned int myPort) { this->myPort_ = myPort; }

    MazeType maze_;
    RatName myName_;
	int dist[MAZEXMAX][MAZEYMAX][MAZEXMAX][MAZEYMAX];    
	bool	scheduleRTUpdate; 
	int		scheduleRTUpdateTimeStamp;	
protected:
	MazewarInstance(string s) : Fwk::NamedInterface(s), dir_(0), dirPeek_(0), myRatId_(0), score_(0),
		xloc_(0), yloc_(0), xPeek_(0), yPeek_(0), messageID_(0), 
		scheduleRTUpdate(FALSE), scheduleRTUpdateTimeStamp(0) {
		myAddr_ = (Sockaddr*)malloc(sizeof(Sockaddr));
		if(!myAddr_) {
			printf("Error allocating sockaddr variable");
		}
	}
	Direction	dir_;
    Direction 	dirPeek_;

    long mazePort_;
    Sockaddr *myAddr_;
    Rat mazeRats_[MAX_RATS];
    RatId myRatId_;

    bool peeking_;
    int theSocket_;
    Score score_;
    Loc xloc_;
    Loc yloc_;
    Loc xPeek_;
    Loc yPeek_;
    int active_;

	unsigned int myIP_;				/* My IP address */
	unsigned int myPort_;			/* My Port number */
	
	unsigned int messageID_;		/* Generating increasing message ID for messages */
	
public:
	unsigned int getMessageID () {
		messageID_++;
		return messageID_;	
	}
};

extern MazewarInstance::Ptr M;

#define REG_INTVL		1000		// Regular status update interval 
#define RT_INTVL		10000		// Scheduled real time update interval
#define MAX_TRIES		3			// Maximum number of tries
#define VPROJ			0.002		// Velocity of the projectiles (grid per millisecond)
#define QUEUETIMEOUT	100			// Timeout value
#define ISALVTIMEOUT	1000		// IsAlive message timeout value
#define MAX_RAT_V		0.005 		// Maximum speed of the rat 

#define MY_RAT_INDEX		0
#define MY_DIR			M->dir().value()
#define MY_X_LOC		M->xloc().value()
#define MY_Y_LOC		M->yloc().value()
#define MY_SCORE		M->score().value()
#define MY_NAME			M->myName_
#define MY_IP			M->myIP()
#define MY_PORT			M->myPort()

/* events */

#define	EVENT_A		1		/* user pressed "A" */
#define	EVENT_S		2		/* user pressed "S" */
#define	EVENT_F		3		/* user pressed "F" */
#define	EVENT_D		4		/* user pressed "D" */
#define	EVENT_BAR	5		/* user pressed space bar */
#define	EVENT_LEFT_D	6		/* user pressed left mouse button */
#define	EVENT_RIGHT_D	7		/* user pressed right button */
#define	EVENT_MIDDLE_D	8		/* user pressed middle button */
#define	EVENT_LEFT_U	9		/* user released l.M.b */
#define	EVENT_RIGHT_U	10		/* user released r.M.b */

#define	EVENT_NETWORK	16		/* incoming network packet */
#define	EVENT_INT	17		/* user pressed interrupt key */
#define	EVENT_TIMEOUT	18		/* nothing happened! */

extern unsigned short	ratBits[];
/* replace this with appropriate definition of your own */
typedef	struct {
	unsigned int	messageID;			/* Message ID of the packet */
	unsigned int 	senderIP;			/* Sender's IP address */
	unsigned int 	senderPort;			/* Sender's Port number */
	unsigned int	receiverIP;			/* Receiver's IP address, used to mimic unicast */
	unsigned int 	receiverPort;		/* Receiver's Port number */
	unsigned int 	type;				/* The type/descriptor header of the packet */
	unsigned int	hash;				/* Security hash code */
	
	u_long	body[16];					/* The packet payload */
}	__attribute__ ((__packed__))	MW244BPacket;

typedef	struct {
	short		eventType;
	MW244BPacket	*eventDetail;	/* for incoming data */
	Sockaddr	eventSource;
}					MWEvent;

/* Status type packet, including regular status update 0x02, realtime status update 0x03, 
	and projectile 0x08 messages.  */
typedef struct {
	unsigned int x;					/* x location */
	unsigned int y;					/* y location */
	unsigned int dir;				/* direction  */
	int	score;						/* score of the player */
}	__attribute__ ((__packed__))	PStatus;

/* Tagged type packet, 0x09 */
typedef struct {
	int score;
	unsigned int ownerIP;			/* The IP address of the owner of projectile */
	unsigned int ownerPort;			/* The Port number of the owner of the projectile */
}	__attribute__ ((__packed__))	PTagged;

/* Join Game type packet, 0x10 */
typedef struct {
	unsigned int x;
	unsigned int y;
	unsigned int dir;
	int	score;
	char name[20];					/* The name of the new player */
}	__attribute__ ((__packed__))	PJoin;

/* Welcome type packet, 0x11 */
typedef struct {
	unsigned int x;
	unsigned int y;
	unsigned int dir;
	int	score;
	char name[20];
	unsigned int IP1;				/* IP address of neighbor player */
	unsigned int Port1;				/* Port number of neighbor player */
	unsigned int IP2;				/* IP address of another neighbor player */
	unsigned int Port2;				/* Port number of another neighbor player */
}	__attribute__ ((__packed__))	PWelcome;

/* IsAliveQuery type packet, 0x20 */
typedef struct {
	unsigned int queryIP;			/* IP address of the player in query */
	unsigned int queryPort;			/* Port number of the player in query */ 
}	__attribute__ ((__packed__))	PIsAliveQuery;

/* Packet types */
#define PACKET_PING		0x00		/* Ping packet */
#define	PACKET_PONG		0x01		/* Pong/ACK packet */
#define PACKET_REG		0x02		/* Regular status update packet */
#define PACKET_RT		0x03		/* Realtime status update packet */
#define	PACKET_PROJ		0x08		/* Projectile packet */
#define PACKET_TAGGED	0x09		/* Tagged packet */
#define PACKET_JOIN		0x10		/* Join Game packet */
#define PACKET_WELCOME	0x11		/* Welcome packet */
#define PACKET_ISALV	0x20		/* IsAliveQuery packet */
#define PACKET_ALV		0x21		/* IsAliveQuery Hit (alive) */
#define PACKET_DEAD		0x22		/* IsAliveQuery Hit (dead) */

/* Message queue instance class. Each object stores information of a message 
   Note that the object of this class could either store messages to be sent
   or messages that waiting for ACK */
class MSGQueueInstance {
public:
	int time;						/* Timestamp of the message. It represents the time that the message should get processed. */
	unsigned int type;				/* Type of the message. It could be transient message 0x00, regular status update 0x01, 
									realtime status update 0x02, messages waiting for ACK 0x10, IsAlive Query or Ping messages 0x11	 */
	int tries;						/* Number of attempts that tried, only valid for message waiting for ACK */
									
	MW244BPacket *content;			/* The content of the message, i.e. packet */
	RatIndexType	receiver;		/* Target receiver of the message */

	MSGQueueInstance *next;			/* Pointer to the next instance in message queue linked list */

	/* timeInc is used to timestamp the message, and it is relative to current time */
	MSGQueueInstance(unsigned int Type, MW244BPacket *Content, RatIndexType Receiver, int timeInc) : receiver(Receiver.value()) {
		time = getCurrentTime() + timeInc;
		type = Type;
		tries = 0;
		content = Content;	
		next = NULL;
	}
	
	/* Copy constructor */
	MSGQueueInstance (const MSGQueueInstance & inst) : receiver(inst.receiver.value()) {
		time = inst.time;
		type = inst.type;
		tries = inst.tries;
		content = new MW244BPacket;
		memcpy (content, inst.content, sizeof (MW244BPacket));
		next = inst.next;
	}
	
};

/* Message types */
#define QTYPE_TRANS		0x00	/* Transient type. The message is discarded after sending */
#define QTYPE_REG		0x01	/* Regular status update type. This kind of messages repeat with timestamps of specified interval */
#define QTYPE_ACK		0x10	/* ACK type, for messages that are waiting for ACK */
#define QTYPE_ISALVPING	0x11	/* IsAlive Query or IsAlive Ping messages */

/* Message queue class, which presents a uniform interface for handling messages, 
	including outgoing messages and messages waiting for ACK etc. The queue is sorted 
	in increasing order with respect to message timestamps */
class MSGQueue {
public:
	MSGQueueInstance *head;		/* Head pointer of the message queue link list */
	MSGQueue () {
		head = NULL;
	}
	
	/* Return the minimum timestamp of the queue */
	int minTime();
	
	/* Add a new message into the queue */
	void enQueue(MSGQueueInstance *instance);

	/* Add a broadcast message into the queue */
	void enQueueAll(MSGQueueInstance *instance);
	
	/* Extract the message with minimum time stamp */
	MSGQueueInstance *deQueue();
	
	/* Extract the message with certain messageID, used for ACK messages */
	MSGQueueInstance *deQueue ( unsigned int messageID, RatIndexType receiver );
	
	/* Cleanup message queue upon exit */
	void freeQueue ();
};

/* Missile instance class. Used to manage missiles */
class MissileInstance {
public:
	Loc x;					/* x location of where it is fired */
	Loc y;					/* y location of where it is fired */
	Direction dir;			/* travel direction */	
	int dx, dy;				/* x and y increment corresponding to dir */
	
	int time;				/* Timestamp of when it is fired */
	int ttl;				/* Time to live, i.e. time takes to hit wall */
	
	MissileInstance (Loc _x, Loc _y, Direction _dir) : x(_x.value()), y(_y.value()), dir(_dir.value()) {
		time = getCurrentTime ();
		
		/* Calculate time to live */
		int i = _x.value();
		int j = _y.value();
		if (_dir.value() == NORTH) {dx = 1; dy = 0;}
		else if (_dir.value() == SOUTH) {dx = -1; dy = 0;}
		else if (_dir.value() == EAST) {dx = 0; dy = 1;}
		else if (_dir.value() == WEST) {dx = 0; dy = -1;}
		
		int step = 0;
		while (M->maze_[i][j] == 0) {
			i += dx;
			j += dy;
			step = step + 1;
		}
		ttl = round(step / VPROJ);
	}
	
	/* Check whether the missile expires */
	bool expire();
	
	/* Check whether I am hit by the missile */
	bool hit();
	
	/* Show the missile on map */
	void show(bool);
};

/* Message queue */
extern MSGQueue queue;

void		*malloc();
Sockaddr	*resolveHost();

/* display.c */
void InitDisplay(int, char **);
void StartDisplay(void);
void ShowView(Loc, Loc, Direction);
void SetMyRatIndexType(RatIndexType);
void SetRatPosition(RatIndexType, Loc, Loc, Direction);
void ClearRatPosition(RatIndexType);
void ShowPosition(Loc, Loc, Direction);
void ShowAllPositions(void);
void showMe(Loc, Loc, Direction);
void clearPosition(RatIndexType, Loc, Loc);
void clearSquare(Loc xClear, Loc yClear);
void NewScoreCard(void);
void UpdateScoreCard(RatIndexType);
void FlipBitmaps(void);
void bitFlip(BitCell *, int size);
void SwapBitmaps(void);
void byteSwap(BitCell *, int size);
void showMissile(Loc, Loc, Direction, Loc, Loc, bool);

/* init.c */
void MazeInit(int, char **);
void ratStates(void);
void getMaze(void);
void setRandom(void);
void getName(char *, char **);
void getString(char *, char **);
void getHostName(char *, char **, Sockaddr *);
Sockaddr *resolveHost(char *);
bool emptyAhead();
bool emptyRight();
bool emptyLeft();
bool emptyBehind();

/* toplevel.c */
void play(void);
void aboutFace(void);
void leftTurn(void);
void rightTurn(void);
bool occupied(int, int);
void forward(void);
void backward(void);
void peekLeft(void);
void peekRight(void);
void peekStop(void);
void shoot(void);
void quit(int);
void NewPosition(MazewarInstance::Ptr M);
void MWError(char *);
RatIndexType getRatID (unsigned int, unsigned int);
bool testGetRatID (unsigned int, unsigned int);
void updateRat (RatIndexType, unsigned int, unsigned int, unsigned int, int, char *, unsigned int);
Score GetRatScore(RatIndexType);
char  *GetRatName(RatIndexType);
void getTwoNeighbors(RatIndexType &, RatIndexType &, RatIndexType);
void ConvertIncoming(MW244BPacket *);
void ConvertOutgoing(MW244BPacket *);
void ratState(void);
void manageMissiles(void);
void DoViewUpdate(void);
void sendPacket(MW244BPacket *, RatIndexType);
void processPacket(MWEvent *);
void netInit(void);
int  getCurrentTime(void);
unsigned int hashFunc(unsigned int, unsigned int, unsigned int);
void DistanceInit(void);
void realTimeUpdate (void);

/* winsys.c */
void InitWindow(int, char **);
void StartWindow(int, int);
void ClearView(void);
void DrawViewLine(int, int, int, int);
void NextEvent(MWEvent *, int);
bool KBEventPending(void);
void HourGlassCursor(void);
void RatCursor(void);
void DeadRatCursor(void);
void HackMazeBitmap(Loc, Loc, BitCell *);
void DisplayRatBitmap(int, int, int, int, int, int);
void WriteScoreString(RatIndexType);
void ClearScoreLine(RatIndexType);
void InvertScoreLine(RatIndexType);
void NotifyPlayer(void);
void StopWindow(void);

#endif
